#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

//1. 自动释放传入的互斥锁（这是为了避免死锁，因为如果不释放锁，
// 其他线程无法获取锁来修改条件变量对应的条件并唤醒等待线程 ）。
//2. 将当前线程挂起，放入与条件变量相关联的等待队列中，进入等待状态。
//3. 直到其他线程通过 pthread_cond_signal 或 pthread_cond_broadcast 唤醒该线程，
// 它才会重新获取传入的互斥锁并从 pthread_cond_wait 函数返回。


// 伪唤醒是指线程在没有被其他线程通过 pthread_cond_signal 或 pthread_cond_broadcast 
// 明确唤醒的情况下，从 pthread_cond_wait 调用中返回

//while（IsFull()）
//当线程被唤醒时，会再次检查 IsFull() 条件，如果条件仍然不满足（比如是伪唤醒导致的），
// 线程会再次调用 pthread_cond_wait 继续等待。这样可以确保只有在条件真正满足时，
// 线程才会执行后续的逻辑，避免因伪唤醒导致的错误行为。

const int defaultcap = 5;

template<typename T>
class BlockQueue
{
private:
	bool IsFull() { return _q.size() >= _cap; }
	bool IsEmpty() { return _q.empty(); }

public:
	BlockQueue(int cap = defaultcap)
		: _cap(cap), _csleep_num(0), _psleep_num(0)
	{
		pthread_mutex_init(&_mutex, nullptr);
		pthread_cond_init(&_full_cond, nullptr);
		pthread_cond_init(&_empty_cond, nullptr);
	}

	void Equeue(const T &in)
	{
		pthread_mutex_lock(&_mutex);
		while(IsFull())//这里用while而不是if是因为要进行重复检测解决伪唤醒问题
		{
			// 应该让生产者线程进行等待
            // 重点1：pthread_cond_wait调用成功，挂起当前线程之前，要先自动释放锁！！
            // 重点2：当线程被唤醒的时候，默认就在临界区内唤醒！要从pthread_cond_wait
            // 成功返回，需要当前线程，重新申请_mutex锁！！！
            // 重点3：如果我被唤醒，但是申请锁失败了？？我就会在锁上阻塞等待！！！
			_psleep_num++;
			std::cout << "生产者，进入休眠了: _psleep_num" <<  _psleep_num << std::endl;
			// 问题1: pthread_cond_wait是函数,有可能失败.
			//正常情况下，pthread_cond_wait 函数不会立即返回(看最上面)
			//但若因传参，权限，内存...问题，函数可能会出错，则会立刻返回
            // 问题2：pthread_cond_wait可能会因为，条件其实不满足，pthread_cond_wait 伪唤醒(最上面)
			pthread_cond_wait(&_full_cond, &_mutex);
			_psleep_num--;
		}
		// 100%确定：队列有空间
        _q.push(in);

		// 临时方案
        // v2
		if(_csleep_num > 0)
		{
			pthread_cond_signal(&_empty_cond);
			std::cout << "唤醒消费者..." << std::endl;
		}
		//这里看下文图片
		// pthread_cond_signal(&_empty_cond); // 可以
        pthread_mutex_unlock(&_mutex); // TODO
        // pthread_cond_signal(&_empty_cond); // 可以
	}

	T Pop()
	{
		//消费者调用
		pthread_mutex_lock(&_mutex);
		//如果队列为空，消费者线程需要等待，直到有生产者向队列中添加元素。
        while (IsEmpty())
        {
            _csleep_num++;
            pthread_cond_wait(&_empty_cond, &_mutex);
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();

		//如果大于 0，说明有生产者线程因为队列已满而处于等待状态
		if(_psleep_num > 0)
        {
			//这里唤醒生产者线程是因为消费者从队列中取出了元素，
			//队列有了空闲位置，可能可以让等待的生产者继续向队列中添加元素。
            pthread_cond_signal(&_full_cond);
            std::cout << "唤醒消费者" << std::endl;
        }

		 // pthread_cond_signal(&_full_cond);
        pthread_mutex_unlock(&_mutex);
        return data;
	}
private:
	std::queue<T> _q;//临界资源
	int _cap;		 //容量大小

	pthread_mutex_t _mutex;
	pthread_cond_t _full_cond;
	pthread_cond_t _empty_cond;

	int _csleep_num;// 消费者休眠的个数
	int _psleep_num;// 生产者休眠的个数
};